package structure

import (
	"errors"
	"fmt"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"github.com/gomodule/redigo/redis"
)

type SafeConn interface {
	redis.Conn
	Transaction() SafeConn
	Watch(key string)
	AutoRelease(serializer ...def.AutoSerializer)
	Connect()
	Begin()
	Commit() error
	Cancel()
}

type safeConn struct {
	redis.Conn
	execute    *Execute
	multi      bool
	retryTimes int

	watchList       []string
	sendCommandList []string
	sendArgList     [][]interface{}

	releaseList []def.AutoSerializer
}

func (conn *safeConn) Connect() {
	if conn.Conn == nil {
		conn.Conn = conn.execute.Redis.Get()
		conn.clear()
	}
}

func (conn *safeConn) clear() {
	conn.multi = false
	conn.retryTimes = 5
	conn.sendArgList = conn.sendArgList[0:0]
	conn.sendCommandList = conn.sendCommandList[0:0]
	conn.watchList = conn.watchList[0:0]
	for _, re := range conn.releaseList {
		if re != nil {
			re.Release()
		}
	}
	conn.releaseList = conn.releaseList[0:0]
}

func (conn *safeConn) Transaction() SafeConn {
	return conn.execute.Transaction()
}

func (conn *safeConn) Close() error {
	var err error
	if conn.Conn != nil {
		conn.Cancel()
		err = conn.Conn.Close()
		conn.Conn = nil
	}
	conn.clear()
	conn.execute.release(conn)
	return err
}

func (conn *safeConn) AutoRelease(serializer ...def.AutoSerializer) {
	for _, re := range serializer {
		if re != nil {
			re.Alloc()
			conn.releaseList = append(conn.releaseList, re)
		}
	}
}

func (conn *safeConn) Send(commandName string, args ...interface{}) error {
	if conn.multi {
		conn.sendCommandList = append(conn.sendCommandList, commandName)
		conn.sendArgList = append(conn.sendArgList, args)
		return nil
	} else {
		return conn.Conn.Send(commandName, args...)
	}
}

func (conn *safeConn) Watch(key string) {
	if conn.multi {
		conn.watchList = append(conn.watchList, key)
	}
}

func (conn *safeConn) Begin() {
	conn.Connect()
	conn.multi = true
	_ = conn.Flush()
}

func (conn *safeConn) Commit() error {
	if conn.multi {
		conn.Connect()
		for {
			if len(conn.watchList) > 0 {
				for _, v := range conn.watchList {
					if err := conn.Conn.Send("WATCH", v); err != nil {
						conn.Cancel()
						fmt.Println("watch", err)
						return err
					}
				}
			}
			if err := conn.Conn.Send("MULTI"); err != nil {
				return err
			}
			for i := 0; i < len(conn.sendCommandList); i++ {
				//fmt.Println(conn.sendCommandList[i], conn.sendArgList[i])
				if err := conn.Conn.Send(conn.sendCommandList[i], conn.sendArgList[i]...); err != nil {
					conn.Cancel()
					return err
				}
			}
			if err := conn.Conn.Send("EXEC"); err != nil {
				conn.Cancel()
				return err
			}
			if err := conn.Conn.Flush(); err != nil {
				conn.Cancel()
				return err
			}
			reply, err := conn.Conn.Receive()
			if err != nil {
				conn.Cancel()
				return err
			}

			if reply != nil {
				break
			}
			conn.retryTimes -= 1
			if conn.retryTimes == 0 {
				conn.Cancel()
				return errors.New("[ Redis ] Retry Times fail")
			}
		}
	} else if conn.Conn != nil {
		err := conn.Flush()
		if err != nil {
			return err
		}
	}
	conn.clear()
	return nil
}

func (conn *safeConn) Cancel() {
	if conn.multi {
		_ = conn.Conn.Send("DISCARD")
		conn.clear()
	}
}

type Execute struct {
	Redis *redis.Pool
	Key   string

	connPool sync.Pool

	DateFactory

	DataMap map[PublicDataType]string
}

func NewExecute(redis *redis.Pool, key string, defined DateFactory) *Execute {
	e := &Execute{
		Redis:    redis,
		Key:      key,
		connPool: sync.Pool{},

		DateFactory: defined,
	}
	e.connPool.New = func() interface{} {
		return &safeConn{
			execute:         e,
			sendArgList:     [][]interface{}{},
			sendCommandList: []string{},
			watchList:       []string{},
			releaseList:     []def.AutoSerializer{},
		}
	}
	return e
}

func (execute *Execute) SetDataType(dataMap map[PublicDataType]string) {
	execute.DataMap = dataMap
}

func (execute *Execute) GetDataType(t PublicDataType) string {
	return execute.DataMap[t]
}

func (execute *Execute) Transaction() SafeConn {
	conn := execute.connPool.Get().(*safeConn)
	return conn
}

func (execute *Execute) release(conn *safeConn) {
	execute.connPool.Put(conn)
}

func (execute *Execute) Traverse(conn SafeConn, t PublicDataType, prefix string, fn func(id interface{})) {
	matchKey := execute.Key + "/" + execute.GetDataType(t) + "/" + prefix
	execute.TraverseAll(conn, matchKey, func(key string) {
		fn(strings.Replace(key, matchKey, "", 1))
	})
}

func (execute *Execute) TraverseAll(conn SafeConn, matchKey string, fn func(key string)) {
	cursor := 0
	for {
		r, err := conn.Do("SCAN", cursor, "MATCH", matchKey+"*", "COUNT", "1000")
		if err != nil {
			break
		}
		rr := r.([]interface{})
		cursor = utils.ToInt(utils.ToString(rr[0]))

		list := rr[1].([]interface{})
		for _, v := range list {
			fn(utils.ToString(v))
		}

		if cursor == 0 {
			break
		}
	}
}

func (execute *Execute) IncrId(conn SafeConn) int {
	c, _ := conn.Do("INCRBY", execute.Key+"/"+"cooperate", 1)
	return utils.ToInt(utils.ToString(c))
}

func (execute *Execute) GetId(conn SafeConn) int {
	c, _ := conn.Do("GET", execute.Key+"/"+"cooperate")
	return utils.ToInt(utils.ToString(c))
}

func (execute *Execute) IncrTypeId(conn SafeConn, t PublicDataType, prefix string) int {
	c, err := conn.Do("INCRBY", execute.Key+"/"+prefix+execute.GetDataType(t), 1)
	if err != nil {
		fmt.Println(err)
	}
	return utils.ToInt(utils.ToString(c))
}

func (execute *Execute) ChangeTypeId(conn SafeConn, t PublicDataType, prefix string, id int) int {
	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"GET\", KEYS[1])\nif (not v1 or tonumber(v1)<tonumber(ARGV[1]))\nthen\nredis.pcall(\"SET\", KEYS[1], ARGV[1])\nend\nreturn v1", 1, execute.Key+"/"+prefix+execute.GetDataType(t), id)
	if err != nil {
		fmt.Println(err)
	}
	return utils.ToInt(utils.ToString(v))
}

func (execute *Execute) GetTypeId(conn SafeConn, t PublicDataType, prefix string) int {
	c, _ := conn.Do("GET", execute.Key+"/"+prefix+execute.GetDataType(t))
	return utils.ToInt(utils.ToString(c))
}

func (execute *Execute) IncrChildId(conn SafeConn, data Data, t2 PublicDataType) int {
	c, _ := conn.Do("HINCRBY", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), execute.GetDataType(t2), 1)
	return utils.ToInt(utils.ToString(c))
}

func (execute *Execute) GetChildId(conn SafeConn, data Data, t2 PublicDataType) int {
	c, _ := conn.Do("HGET", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), execute.GetDataType(t2))
	return utils.ToInt(utils.ToString(c))
}

func (execute *Execute) AddDepend(conn SafeConn, data1 Data, data2 Data, beDepend string, depend string) {
	//fmt.Println("add depend", data1, data2, beDepend, depend)
	//push
	key := execute.Key + "/" + execute.GetDataType(data1.GetDataType()) + utils.ToString(data1.GetId()) + "/" + beDepend
	//conn.Watch(key)
	//fmt.Println("add depend", key)
	if err := conn.Send("RPUSH", key, data2.GetId()); err != nil {
		fmt.Println("add depend", err)
	}

	key = execute.Key + "/" + execute.GetDataType(data2.GetDataType()) + "/" + utils.ToString(data2.GetId())
	//conn.Watch(key)
	//fmt.Println("add depend", key)
	if err := conn.Send("HSET", key, depend, data1.GetId()); err != nil {
		fmt.Println("add depend", err)
	}
	//key = execute.Key + "/" + execute.GetDataType(t1) + "/" + utils.ToString(id1)
	//conn.Watch(key)
	//_ = conn.Send("HINCRBY", key, execute.GetDataType(t2)+"number", 1)
}

func (execute *Execute) RemoveDepend(conn SafeConn, data1 Data, data2 Data, beDepend string, depend string) {
	//lrem 0, id
	key := execute.Key + "/" + execute.GetDataType(data1.GetDataType()) + utils.ToString(data1.GetId()) + "/" + beDepend

	//conn.Watch(key)
	//fmt.Println("remove depend", key)
	_ = conn.Send("LREM", key, 0, data2.GetId())
	key = execute.Key + "/" + execute.GetDataType(data2.GetDataType()) + "/" + utils.ToString(data2.GetId())
	//conn.Watch(key)
	//fmt.Println("remove depend", key)
	_ = conn.Send("HDEL", key, depend)
	//key = execute.Key + "/" + execute.GetDataType(t1) + "/" + utils.ToString(id1)
	//conn.Watch(key)
	//_ = conn.Send("HINCRBY", key, execute.GetDataType(t2)+"number", -1)
}

func (execute *Execute) ClearDepend(conn SafeConn, data Data, beDepend string, dataType PublicDataType, depend string) {
	list := execute.GetDepends(conn, data, beDepend)
	if list == nil {
		return
	}
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + utils.ToString(data.GetId()) + "/" + beDepend
	//conn.Watch(key)
	_ = conn.Send("DEL", key)
	for _, id2 := range list {
		key = execute.Key + "/" + execute.GetDataType(dataType) + "/" + utils.ToString(id2)
		//conn.Watch(key)
		_ = conn.Send("HDEL", key, depend)
	}
}

func (execute *Execute) ExistRemoveDepend(conn SafeConn, data1 Data, data2 Data, dependKey string) bool {
	r, _ := conn.Do("LREM", execute.Key+"/"+execute.GetDataType(data1.GetDataType())+utils.ToString(data1.GetId())+"/"+dependKey, 0, data2.GetId())
	return utils.ToInt(utils.ToString(r)) > 0
}

func (execute *Execute) GetDepends(conn SafeConn, data Data, beDepend string) []interface{} {
	//lrange 0,-1
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + utils.ToString(data.GetId()) + "/" + beDepend
	//fmt.Println("get depens", key)
	c, _ := conn.Do("LRANGE", key, 0, -1)
	if c == nil {
		return nil
	}
	//fmt.Println(data, key, utils.ToBytesSlice(c))
	return utils.ToInterfaceSlice(utils.ToStringSlice(c.([]interface{})))
}

func (execute *Execute) GetExtendKey(data Data, et ExtendType) string {
	return execute.Key + "/" + execute.GetDataType(data.GetDataType()) + utils.ToString(data.GetId()) + "/" + string(et)
}

func (execute *Execute) DeleteExtend(conn SafeConn, data Data, et ExtendType) {
	key := execute.GetExtendKey(data, et)
	//conn.Watch(key)
	_ = conn.Send("DEL", key)
}

func (execute *Execute) Create(conn SafeConn, data Data, info map[string]interface{}) string {
	//hmset
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	//fmt.Println("create", key)
	//conn.Watch(key)
	args := []interface{}{}
	args = append(args, key)
	for key, value := range info {
		args = append(args, key, value)
	}
	if _, err := conn.Do("HMSET", args...); err != nil {
		fmt.Println("HMSET", err, args)
	}
	return key
}

func (execute *Execute) Update(conn SafeConn, data Data, info map[string]interface{}) string {
	//fmt.Println("update", data)
	//HMSET
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	//fmt.Println("update", key)
	conn.Watch(key)
	args := []interface{}{}
	args = append(args, key)
	for key, value := range info {
		args = append(args, key, value)
	}
	if err := conn.Send("HMSET", args...); err != nil {
		fmt.Println("HMSET", err, args)
	}
	return key
}

func (execute *Execute) Delete(conn SafeConn, data Data) {
	//del
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	//conn.Watch(key)
	_ = conn.Send("DEL", key)
}

func (execute *Execute) Get(conn SafeConn, data Data) map[string]interface{} {
	//hgetall
	c, err := conn.Do("HGETALL", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()))
	if err != nil {
		fmt.Println(err)
	}
	if c == nil {
		return nil
	}
	m := map[string]interface{}{}
	cc := c.([]interface{})
	for i := 0; i < len(cc); i += 2 {
		k := utils.ToString(cc[i])
		v := utils.ToString(cc[i+1])
		m[k] = v
	}
	return m
}

func (execute *Execute) GetAndDel(conn SafeConn, data Data) map[string]interface{} {
	//hgetall
	c, _ := conn.Do("EVAL", "local v1 = redis.pcall(\"HGETALL\", KEYS[1])\nredis.pcall(\"DEL\", KEYS[1])\nreturn v1", 1, execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()))
	if c == nil {
		return nil
	}
	m := map[string]interface{}{}
	cc := c.([]interface{})
	for i := 0; i < len(cc); i += 2 {
		k := utils.ToString(cc[i])
		v := utils.ToString(cc[i+1])
		m[k] = v
	}
	return m
}

func (execute *Execute) GetProp(conn SafeConn, data Data, prop string) interface{} {
	c, _ := conn.Do("HGET", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), prop)
	if c == nil {
		return nil
	}
	return utils.ToString(c)
}

func (execute *Execute) GetProps(conn SafeConn, data Data, props []string) map[string]interface{} {
	startTime := time.Now()
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	args := []interface{}{}
	args = append(args, key)
	for _, value := range props {
		args = append(args, value)
	}
	c, err := conn.Do("HMGET", args...)
	if c == nil || err != nil {
		fmt.Println("get props", data, props, err)
		debug.PrintStack()
		return nil
	}
	m := map[string]interface{}{}
	cc := c.([]interface{})
	ok := false
	for i := 0; i < len(cc); i += 1 {
		k := props[i]
		if cc[i] == nil {
			m[k] = nil
			continue
		}
		ok = true
		v := utils.ToString(cc[i])
		m[k] = v
	}
	if !ok {
		return nil
	}
	endTime := time.Now()
	dd := endTime.Sub(startTime)
	if dd > 50*time.Millisecond {
		fmt.Println("get", data, dd)
	}
	//fmt.Println(t, id, m)
	return m
}

func (execute *Execute) SetProp(conn SafeConn, data Data, prop string, value interface{}) {
	//fmt.Println("set", execute.Key+"/"+execute.GetDataType(t)+"/"+utils.ToString(id), prop, value)
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	//fmt.Println("set prop", key, prop, value)
	if err := conn.Send("HSET", key, prop, value); err != nil {
		fmt.Println("set prop", err)
	}
}
func (execute *Execute) SetPropSync(conn SafeConn, data Data, prop string, value interface{}) interface{} {
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	//fmt.Println("set prop sync", key, prop, value)
	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], ARGV[2])\nreturn v1", 1, key, prop, value)
	//fmt.Println("incrPropLesser", r)
	if err != nil {
		fmt.Println("SetPropSync", err)
	}
	return v
}
func (execute *Execute) SetPropGreater(conn SafeConn, data Data, prop string, value int) {
	//fmt.Println("incr", t, id, prop, value)
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	err := conn.Send("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = tonumber(ARGV[2])\nif (v1 < v2))\nthen\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nend", 1, k, prop, value)
	//fmt.Println("incrPropGreater", r)
	if err != nil {
		fmt.Println("SetPropGreater", err)
	}
}
func (execute *Execute) SetPropLesser(conn SafeConn, data Data, prop string, value int) {
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	err := conn.Send("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = tonumber(ARGV[2])\nif (v1 > v2))\nthen\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nend", 1, k, prop, value)
	//fmt.Println("incrPropGreater", r)
	if err != nil {
		fmt.Println("SetPropLesser", err)
	}
}
func (execute *Execute) SetPropGreaterSync(conn SafeConn, data Data, prop string, value int) (int, int) {
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = tonumber(ARGV[2])\nif (v2 > v1)\nthen\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nreturn {v2, v1}\nend\nreturn {v1, v1}", 1, k, prop, value)
	//fmt.Println("incrPropLesser", r)
	if err != nil {
		fmt.Println("SetPropGreaterSync", err)
	}
	vv := utils.ToIntSlice(v)
	return vv[0], vv[1]
}
func (execute *Execute) SetPropLesserSync(conn SafeConn, data Data, prop string, value int) (int, int) {
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = tonumber(ARGV[2])\nif (v2 < v1)\nthen\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nreturn {v2, v1}\nend\nreturn {v1, v1}", 1, k, prop, value)
	//fmt.Println("incrPropLesser", r)
	if err != nil {
		fmt.Println("SetPropLesserSync", err)
	}
	vv := utils.ToIntSlice(v)
	return vv[0], vv[1]
}

func (execute *Execute) IncrProp(conn SafeConn, data Data, prop string, value int) {
	//fmt.Println("incr", t, id, prop, value)
	_ = conn.Send("HINCRBY", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), prop, value)
}
func (execute *Execute) IncrPropSync(conn SafeConn, data Data, prop string, value int) int {
	v, err := conn.Do("HINCRBY", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), prop, value)
	if err != nil {
		fmt.Println("IncrProp", err)
	}
	return utils.ToInt(v)
}

func (execute *Execute) IncrPropGreater(conn SafeConn, data Data, prop string, value int, greater int) {
	//fmt.Println("incr", t, id, prop, value)
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	err := conn.Send("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + ARGV[2]\nif (v2 <tonumber(ARGV[3]))\nthen\nv2=ARGV[3]\nend\nreturn redis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)", 1, k, prop, value, greater)
	//fmt.Println("incrPropGreater", r)
	if err != nil {
		fmt.Println("IncrPropGreater", err)
	}
}

func (execute *Execute) IncrPropLesser(conn SafeConn, data Data, prop string, value int, lesser int) {
	//fmt.Println("incr", t, id, prop, value)
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	err := conn.Send("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + ARGV[2]\nif (v2 >tonumber(ARGV[3]))\nthen\nv2=ARGV[3]\nend\nreturn redis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)", 1, k, prop, value, lesser)
	//fmt.Println("incrPropLesser", r)
	if err != nil {
		fmt.Println("incrPropLesser", err)
	}
}

func (execute *Execute) IncrPropGreaterSync(conn SafeConn, data Data, prop string, value int, greater int) int {
	//fmt.Println("incr", t, id, prop, value)
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + tonumber(ARGV[2])\nif (v2 <tonumber(ARGV[3]))\nthen\nv2=tonumber(ARGV[3])\nend\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nreturn v2", 1, k, prop, value, greater)
	//fmt.Println("incrPropGreater", r)
	if err != nil {
		fmt.Println("IncrPropGreater", err)
		return 0
	}
	return utils.ToInt(v)
}

func (execute *Execute) IncrPropLesserSync(conn SafeConn, data Data, prop string, value int, lesser int) int {
	//fmt.Println("incr", t, id, prop, value)
	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + tonumber(ARGV[2])\nif (v2 >tonumber(ARGV[3]))\nthen\nv2=tonumber(ARGV[3])\nend\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nreturn v2", 1, k, prop, value, lesser)
	//fmt.Println("incrPropLesser", r)
	if err != nil {
		fmt.Println("incrPropLesser", err)
	}
	return utils.ToInt(v)
}

//
//func (execute *Execute) IncrProps(conn SafeConn, data Data, prop []string, value []int) {
//	//fmt.Println("incr", t, id, prop, value)
//	_ = conn.Send("HINCRBY", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), prop, value)
//}
//func (execute *Execute) IncrPropsSync(conn SafeConn, data Data, prop []string, value []int) int {
//	v, err := conn.Do("HINCRBY", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()), prop, value)
//	if err != nil {
//		fmt.Println("IncrProp", err)
//	}
//	return utils.ToInt(v)
//}
//
//func (execute *Execute) IncrPropsGreater(conn SafeConn, data Data, props []string, value []int, greater int) {
//	//fmt.Println("incr", t, id, prop, value)
//	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
//	err := conn.Send("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + ARGV[2]\nif (v2 <tonumber(ARGV[3]))\nthen\nv2=ARGV[3]\nend\nreturn redis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)", 1, k, prop, value, greater)
//	//fmt.Println("incrPropGreater", r)
//	if err != nil {
//		fmt.Println("IncrPropGreater", err)
//	}
//}
//
//func (execute *Execute) IncrPropsLesser(conn SafeConn, data Data, props []string, value []int, lesser int) {
//	//fmt.Println("incr", t, id, prop, value)
//	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
//	err := conn.Send("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + ARGV[2]\nif (v2 >tonumber(ARGV[3]))\nthen\nv2=ARGV[3]\nend\nreturn redis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)", 1, k, prop, value, lesser)
//	//fmt.Println("incrPropLesser", r)
//	if err != nil {
//		fmt.Println("incrPropLesser", err)
//	}
//}
//
//func (execute *Execute) IncrPropsGreaterSync(conn SafeConn, data Data, prop []string, value []int, greater int) []int {
//	//fmt.Println("incr", t, id, prop, value)
//	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
//	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + tonumber(ARGV[2])\nif (v2 <tonumber(ARGV[3]))\nthen\nv2=tonumber(ARGV[3])\nend\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nreturn v2", 1, k, prop, value, greater)
//	//fmt.Println("incrPropGreater", r)
//	if err != nil {
//		fmt.Println("IncrPropGreater", err)
//		return 0
//	}
//	return utils.ToInt(v)
//}
//
//func (execute *Execute) IncrPropsLesserSync(conn SafeConn, data Data, prop []string, value []int, lesser int) []int {
//	//fmt.Println("incr", t, id, prop, value)
//	k := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
//	v, err := conn.Do("EVAL", "local v1 = redis.pcall(\"HGET\", KEYS[1], ARGV[1])\nlocal v2 = v1 + tonumber(ARGV[2])\nif (v2 >tonumber(ARGV[3]))\nthen\nv2=tonumber(ARGV[3])\nend\nredis.pcall(\"HSET\", KEYS[1], ARGV[1], v2)\nreturn v2", 1, k, prop, value, lesser)
//	//fmt.Println("incrPropLesser", r)
//	if err != nil {
//		fmt.Println("incrPropLesser", err)
//	}
//	return utils.ToInt(v)
//}
func (execute *Execute) DelProp(conn SafeConn, data Data, prop string) {
	args := []interface{}{execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())}
	args = append(args, prop)
	_ = conn.Send("HDEL", args...)
}
func (execute *Execute) DelProps(conn SafeConn, data Data, prop []string) {
	args := []interface{}{execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())}
	for _, value := range prop {
		args = append(args, value)
	}
	_ = conn.Send("HDEL", args...)
}

func (execute *Execute) DelPropsMatch(conn SafeConn, data Data, match string) {

}

func (execute *Execute) GetBytes(conn SafeConn, data Data) []byte {
	c, _ := conn.Do("GET", execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()))
	if c == nil {
		return nil
	}
	return utils.ToBytes(utils.ToString(c))
}

func (execute *Execute) CreateBytes(conn SafeConn, data Data, b []byte, expire int) string {
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + "/" + utils.ToString(data.GetId())
	//fmt.Println("createbytes", key)
	if b == nil || len(b) == 0 {
		fmt.Println("create bytes empty", key)
	}
	//conn.Watch(key)
	if expire > 0 {
		if err := conn.Send("SETEX", key, expire, b); err != nil {
			fmt.Println("SETEX", err)
		}
	} else {
		if err := conn.Send("SET", key, b); err != nil {
			fmt.Println("SET", err)
		}
	}
	return key
}

func (execute *Execute) GetAndDelBytes(conn SafeConn, data Data) []byte {
	c, _ := conn.Do("EVAL", "local v1 = redis.pcall(\"GET\", KEYS[1])\nredis.pcall(\"DEL\", KEYS[1])\nreturn v1", 1, execute.Key+"/"+execute.GetDataType(data.GetDataType())+"/"+utils.ToString(data.GetId()))
	if c == nil {
		return nil
	}
	return utils.ToBytes(utils.ToString(c))
}

func (execute *Execute) GetBytesBatch(conn SafeConn, data ...Data) [][]byte {
	ll := make([]interface{}, len(data))
	for index, d := range data {
		ll[index] = execute.Key + "/" + execute.GetDataType(d.GetDataType()) + "/" + utils.ToString(d.GetId())
	}
	c, _ := conn.Do("MGET", ll...)
	if c == nil {
		return nil
	}
	v := utils.ToBytesSlice(utils.ToStringSlice(c))
	return v
}

func (execute *Execute) GetAndDelBytesBatch(conn SafeConn, data ...Data) [][]byte {
	ll := make([]interface{}, len(data))
	for index, d := range data {
		ll[index] = execute.Key + "/" + execute.GetDataType(d.GetDataType()) + "/" + utils.ToString(d.GetId())
	}
	c, err := conn.Do("MGET", ll...)
	if err != nil {
		fmt.Println(err)
	}
	if c == nil {
		return nil
	}
	v := utils.ToBytesSlice(utils.ToStringSlice(c))
	_ = conn.Send("DEL", ll...)
	return v
}
